/*******************************************************************************
 * *
 * *             COPYRIGHT 2012 CLEARLEAP, INC. ALL RIGHTS RESERVED.
 * *             CLEARLEAP CONFIDENTIAL PROPRIETARY
 * *             TEMPLATE VERSION R01.00
 * *
 * ********************************************************************************
 * *
 * *  FILE NAME: ClearleapUtil.java
 * *
 * *---------------------------------- PURPOSE ------------------------------------
 * *
 * * Define common utilities used by system
 * *
 * *
 * *----------------------------- REVISION HISTORY --------------------------------
 * *
 * * Date      ID        Tracking#      Description
 * * --------  --------  -------------  -------------------------------------------
 * * 01.07                              Created
 * *
 * *--------------------------- INCLUDES ----------------------------*
 * *
 ******************************************************************************/

package com.easysale.util;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TimeZone;
import java.util.logging.Logger;
import java.util.zip.DeflaterOutputStream;

import org.apache.commons.codec.binary.Base64;

public class ClearleapUtil {

    protected static Logger logger = Logger.getLogger("Util");
    // used for creating a randomly generated string
    private static Random random = new Random();
    private static final char[] charMapping = {'a', 'b', 'c', 'd', 'e', 'f',
            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p' };

    private static DateFormat dateTimeFormat = null;

    public static synchronized DateFormat getDateTimeFormat() {
        if (dateTimeFormat == null) {
            dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
            dateTimeFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        }

        return dateTimeFormat;
    }

    private static final int ASSERTION_NOT_BEFORE_MINUTES = -5;
    private static final int ASSERTION_NOT_ON_OR_AFTER_MINUTES = 10;

    /**
     * Returns a String containing the contents of the file located at the
     * specified path.
     *
     * @param path
     *            location of file to be read
     * @return String containing contents of file, null if error reading file
     * @throws IOException
     */
    public static String readFileContents(String path) throws Exception {
        StringBuffer contents = new StringBuffer();
        BufferedReader input = null;
        try {
            input = new BufferedReader(new FileReader(new File(path)));
            String line = null;
            while ((line = input.readLine()) != null) {
                contents.append(line);
            }
            input.close();
            return contents.toString();
        } catch (FileNotFoundException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        }
    }

    public static Map<String, String> readParameters(String path)
            throws Exception {

        Map<String, String> contents = new HashMap<String, String>();
        BufferedReader input = null;
        try {
            input = new BufferedReader(new FileReader(new File(path)));
            String line = null;
            while ((line = input.readLine()) != null) {
                String[] lines = line.split("=");
                if (lines.length > 1) {
                    contents.put(lines[0], lines[1]);
                } else if (lines.length == 1) {
                    contents.put(lines[0], "");
                }
            }
            input.close();
            return contents;
        } catch (FileNotFoundException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        }
    }

    /**
     * Create a randomly generated string conforming to the xsd:ID datatype.
     * containing 160 bits of non-cryptographically strong pseudo-randomness, as
     * suggested by SAML 2.0 core 1.2.3. This will also apply to version 1.1
     *
     * @return the randomly generated string
     */
    public static String createID() {
        byte[] bytes = new byte[20]; // 160 bits
        random.nextBytes(bytes);

        char[] chars = new char[40];

        for (int i = 0; i < bytes.length; i++) {
            int left = (bytes[i] >> 4) & 0x0f;
            int right = bytes[i] & 0x0f;
            chars[i * 2] = charMapping[left];
            chars[i * 2 + 1] = charMapping[right];
        }

        return String.valueOf(chars);
    }

    /**
     * Gets the current date and time in the format specified by xsd:dateTime in
     * UTC form, as described in SAML 2.0 core 1.3.3 This will also apply to
     * Version 1.1.
     *
     * @return the date and time as a String
     */
    public static String getDateAndTime() {
        Date date = new Date();
        return ClearleapUtil.getDateTimeFormat().format(date);
    }

    /**
     * Gets the date and time for the beginning of the Assertion time interval
     * in as specified by SAML v2.0 section 2.5.1.
     *
     * @return the date and time as a String
     */
    public static String getNotBeforeDateAndTime() {
        Calendar beforeCal = Calendar.getInstance();
        beforeCal.add(Calendar.MINUTE, ASSERTION_NOT_BEFORE_MINUTES);
        return ClearleapUtil.getDateTimeFormat().format(beforeCal.getTime());
    }

    /**
     * Gets the date and time for the end of the Assertion time interval in as
     * specified by SAML v2.0 section 2.5.1.
     *
     * @return the date and time as a String
     */
    public static String getNotOnOrAfterDateAndTime() {
        Calendar afterCal = Calendar.getInstance();
        afterCal.add(Calendar.MINUTE, ASSERTION_NOT_ON_OR_AFTER_MINUTES);
        return ClearleapUtil.getDateTimeFormat().format(afterCal.getTime());
    }

    /**
     * Generates an encoded and compressed String from the specified
     * XML-formatted String. The String is encoded in the following order:
     * <p>
     * 1. URL encode <br>
     * 2. Base64 encode <br>
     * 3. Deflate <br>
     *
     * @param xmlString
     *            XML-formatted String that is to be encoded
     * @return String containing the encoded contents of the specified XML
     *         String
     */
    public static String encodeMessage(String xmlString) throws IOException {
        byte[] xmlBytes = xmlString.getBytes("UTF-8");
        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(
                byteOutputStream);
        deflaterOutputStream.write(xmlBytes, 0, xmlBytes.length);
        deflaterOutputStream.close();

        Base64 base64Encoder = new Base64();
        byte[] base64EncodedByteArray = base64Encoder.encode(byteOutputStream
                .toByteArray());
        String base64EncodedMessage = new String(base64EncodedByteArray);

        String encodedString = URLEncoder.encode(base64EncodedMessage, "UTF-8");
        return encodedString;
    }

    /**
     * Returns HTML encoded version of the specified String s.
     *
     * @param s
     *            String to be HTML encoded
     * @return HTML encoded String
     */
    public static String htmlEncode(String s) {
        StringBuffer encodedString = new StringBuffer("");
        char[] chars = s.toCharArray();
        for (char c : chars) {
            if (c == '<') {
                encodedString.append("&lt;");
            } else if (c == '>') {
                encodedString.append("&gt;");
            } else if (c == '\'') {
                encodedString.append("&apos;");
            } else if (c == '"') {
                encodedString.append("&quot;");
            } else if (c == '&') {
                encodedString.append("&amp;");
            } else {
                encodedString.append(c);
            }
        }
        return encodedString.toString();
    }
}
